【X68000(Z)アセンブラ講座 第021回 DMA(アレイモード)によるCG画像高速描画】 2025/06/14   こんにちは!! 前回のプログラムはDMA標準モードで全画面サイズの画像を描画しました。 フルサイズの画像を楽しむことはできましたか? 今回は全画面より小さい画像を画面の指定の位置に表示して楽しむために ワンランク上のDMAアレイモードのプログラムを解説します。 サンプルプログラムを動かす前に 256×256ピクセル8ビットカラー(256色)のBMP画像ファイルを用意して 'BM_TEST6.bmp'と言うファイル名で保存して下さい。 破線の内側を'DMA_A.s'と言うファイル名で保存して下さい。 ------------------------------------------------------------------------------------------------ ******************************************************************************** * * アプリ名 : DMA_A.x * * DMA(アレイチェインモード)を使ってCG画像高速描画アプリ * * Ver1.00 * ******************************************************************************** include A:\XC\INCLUDE\DOSCALL.MAC include A:\XC\INCLUDE\IOCSCALL.MAC * システム領域のアドレス cg_palette equ $E82000 * CG用パレットデータの先頭アドレス dmaptr equ $e84080 * DMAレジスター2の先頭アドレス * 以下DMAレジスターアドレスの先頭からのオフセット(距離) csr equ 0 cer equ 1 spare1 equ 2 dcr equ 4 ocr equ 5 scr equ 6 ccr_ equ 7 spare2 equ 8 mtc equ 10 mar equ 12 space3 equ 16 dar equ 20 spare4 equ 24 btc equ 26 bar equ 28 spare5 equ 32 spare6 equ 36 niv equ 37 spare7 equ 38 eiv equ 39 spare8 equ 40 mfc equ 41 spare9 equ 42 spare10 equ 44 cpr equ 45 spare11 equ 46 spare12 equ 48 dfc_ equ 49 spare13 equ 50 spare14 equ 54 spare15 equ 56 bfc equ 57 spare16 equ 58 spare17 equ 62 gcr equ 63 .cpu 68000 * CPUのタイプ .data * 以降のデータはデータセクションに配置される file00: dc.b 'BM_TEST6.bmp',0 * 読み込むBMPのファイル名 msg_return: dc.b 13,10,0 * 改行コード('13'+'10') & 終端コード('0') msg01: dc.b 'BMP File Name : ',0 msg02: dc.b 'File Open Error !',13,10,0 .even * '.even'以降の偶数アドレスにデータを置く fh: dc.w 0 * ファイルハンドル(ファイル管理番号) dma_x: dc.l 0 dma_y: dc.l 0 dma_width: dc.l 0 dma_height dc.l 0 bmp_header_start: * bmp_header_endまでBMPファイルのヘッダーデータ領域 BMFH: * BITMAPFILEHEADER ビットマップファイルヘッダー bmph: dc.b 0, 0 * 'B'と'M'ならばBMP形式 dc.l 0 dc.w 0 dc.w 0 dc.l 0 BMIH: * BITMAPINFOHEADER ビットマップインフォヘッダー biSize: dc.l 0 biWidth: dc.w 0, 0 * 横ピクセル数 biHeight: dc.w 0, 0 * 縦ピクセル数 biPlanes: dc.w 0 * 1 biBitCount: dc.w 0 * カラーモード biCompression: dc.l 0 * BI_RGB biSizeImage: dc.l 0 biXPelsPerMeter:dc.l 0 biYPelsPerMeter:dc.l 0 biClrUsed: dc.w 0, 0 * 使用されている色の数 biClrImportant: dc.l 0 bmp_header_end: * BMPファイルのヘッダー領域の終わり .bss * '.bss'以降データバッファ BMPal ds.b 1024 * パレットデータバッファ(4*256byte確保する) bmtemp: ds.b 65536 * ピクセルデータ読み込みバッファ(512*512byte確保する) pic ds.b 524288 * VRAMにDMA転送して表示する画像データ(512*512*2byte) dma_array_table:ds.b 1536 * 転送情報テーブル (4 + 2) * 256 .text * '.text'以降のプログラムはテキストセクションに配置される start: * スタートアドレス * ユーザーモードからスーパーバイザーモードに移行する moveq.l #_B_SUPER,d0 * d0にIOCS(BIOS)機能番号を代入 move.l #0,d1 * 数値0をd1に代入 movea.l d1,a1 * d1をa1にコピー trap #15 * IOCSコール実行 * 画面モードの設定 moveq.l #_CRTMOD,d0 * 画面モードの設定 move.w #8,d1 * 画面モードの番号 : 16 = 768x512x4 / 8 = 512x512x8 trap #15 * 画面をクリアして表示をオンにする moveq.l #_G_CLR_ON,d0 * 画面をクリアして表示をオンにする trap #15 pea msg01 * 文字列データmsg01のアドレスをスタックに積む dc.w _PRINT * 文字列の表示 addq.l #4,sp * spの位置を元に戻す pea file00 * ファイル名の先頭アドレスをスタックに積む dc.w _PRINT addq.l #4,sp pea msg_return * 改行するために改行コードのアドレスをスタックに積む dc.w _PRINT addq.l #4,sp bsr load_bmp * BMPファイル読み込みルーチンの呼び出し bsr set_palette * CGパレット設定ルーチンの呼び出し * ファイルから読み込んだ8ビットの画像データをX68Kの16ビットピクセル幅に変換 bsr init_pic * DMA転送描画に使う配列データの準備 move.l #64,d0 * 描画開始X座標(0-256の範囲で変更可能。むしろ変更してみて。) move.l #64,d1 * 描画開始Y座標(0-256の範囲で変更可能。むしろ変更してみて。) move.l #256,d2 * 横描画ピクセル数 move.l #256,d3 * 縦描画ピクセル数 bsr set_dma_table loop: * DMAによる画像高速描画 bsr dma_setup bsr dma_start bsr dma_wait_complete * ESCキーのチェック moveq.l #_BITSNS,d0 moveq.l #0,d1 trap #15 and.b #%00000010,d0 tst.b d0 bne end bra loop end: moveq.l #_CRTMOD,d0 move.w #16,d1 trap #15 * ユーザーモードに戻る moveq.l #_B_SUPER,d0 move.l SP,a1 trap #15 * アプリ終了 dc.w _EXIT * プログラムの終了 load_bmp: * BMPファイルのデータを読み込むルーチン * File Open (ファイルアクセス開始) move.w #0,-(sp) * アクセスモードを指定。0 = 読み込みモード/1 = 書き込みモード pea file00 * ファイル名が書かれた部分の先頭アドレスをスタックに積む dc.w _OPEN * OSコールの実行 : ファイルのオープン addq.l #6,sp * SPの位置を元に戻す tst.l d0 * もしd0がプラスなら、 bpl load_bmp_0 * ラベルload_bmp_0に飛ぶ。 pea msg02 * エラーメッセージ dc.w _PRINT addq.l #4,sp dc.w _EXIT * OSコール実行 : エラーによりソフトウェアの終了 load_bmp_0: move.w d0,fh * _OPENを実行した後にd0にファイルハンドルが入っているので * d0のファイル管理番号をfhに保存する * BMFH read move.l #14,-(sp) * 読み込みバイト数をスタックに積む pea BMFH * アドレスBMFHをスタックに積む move.w fh,-(sp) * ファイルハンドルをスタックに積む dc.w _READ * OSコール実行 : ファイルの読み込み add.l #10,sp * SPの位置を元に戻す load_bmp_1: * BMIH read move.l #40,-(sp) * 40byteのファイルヘッダー pea BMIH move.w fh,-(sp) dc.w _READ * ファイル読み込みのOSコールを実行する lea 10(sp),sp * BMP パレットの読み込み move.l #1024,-(sp) * パレットデータのサイズをスタックに積む pea BMPal move.w fh,-(sp) dc.w _READ lea 10(sp),sp * BMP ピクセルデータの読み込み move.l #65536,-(sp) * ピクセルデータの全てのサイズをスタックに積む pea bmtemp * bmp = 読み込んだピクセルデータを配置するアドレス move.w fh,-(sp) dc.w _READ lea 10(sp),sp * File Close (ファイルアクセス終了) move.w fh,-(sp) dc.w _CLOSE addq.l #2,sp rts * サブルーチンの最後に配置(呼出元にリターンし次の命令から実行再開) * RGBQUAD { Blue8, Green8, Red8, Reserved8 } = %RRRRRRRR_GGGGGGGG_BBBBBBBB_AAAAAAAA * palette { Green5, Red5, Blue5, Alpha1 } = %GGGGGRRR_RRBBBBB0 * Set CG Palette set_palette: movea.l #cg_palette,a0 * CG用パレットレジスターアドレス lea BMPal,a1 * パレットデータバッファアドレス moveq.l #0,d7 set_palette_1: move.b (a1),d0 * a1が示すアドレスからd0にバイトデータを読み込む * ↓32ビット色の青色成分8ビットのうち上位5ビットを抜き取り16ビット色の青色成分として使う lsr.w #2,d0 * 青色ビットデータの位置を16ビット色内の青色位置に右シフト and.w #%0000000000111110,d0 * 16ビット色の青色成分以外のビットを0にする move.b 1(a1),d1 * a1に1を足したアドレスからd1にバイトデータを読み込む * ↓32ビット色の緑色成分8ビットのうち上位5ビットを抜き取り16ビット色の緑色成分として使う lsl.w #8,d1 * 緑色ビットデータの位置を16ビット色内の緑色位置に左シフト and.w #%1111100000000000,d1 * 16ビット色の緑色成分以外のビットを0にする move.b 2(a1),d2 * a1に2を足したアドレスからd2にバイトデータを読み込む * ↓32ビット色の赤色成分8ビットのうち上位5ビットを抜き取り16ビット色の赤色成分として使う lsl.w #3,d2 * 赤色ビットデータの位置を16ビット色内の赤色位置に左シフト and.w #%0000011111000000,d2 * 16ビット色の赤色成分以外のビットを0にする * 青赤緑のそれぞれの色成分を論理和命令で一つの16ビットカラーに完成させる or.w d1,d0 or.w d2,d0 * 完成した色データをパレットメモリーに書き込んで更新する move.w d0,(a0)+ * 16ビットデータを書き込んだ後にa0に2を足す * 次の変換元色データを取り出す前にa1のアドレスを更新しておく addq.l #4,a1 addq.w #1,d7 * 色番号を次の番号に更新する cmp.w #256,d7 * 現在の色番号と256を比較する blt set_palette_1 * 現在の色番号が256未満ならアドレスset_palette_1に飛ぶ rts * 呼出元に戻る * 1ピクセルあたり8ビットのBMP形式から1ピクセルあたり16ビットのX68K形式に変換する init_pic: lea bmtemp,a0 lea pic,a1 moveq.l #0,d7 init_pic_1: move.b (a0)+,d0 move.w d0,(a1)+ addq.l #1,d7 cmp.l #65536,d7 * 65536 = 256 x 256 blt init_pic_1 rts set_dma_table: * in d0.l = 描画開始X座標 * in d1.l = 描画開始Y座標 * in d2.l = 横描画ピクセル数 * in d3.l = 縦描画ピクセル数 movem.l d0/d1/d7/a0/a1,-(sp) . move.l d0,dma_x move.l d1,dma_y move.l d2,dma_width move.l d3,dma_height * 描画開始VRAMアドレスの計算 move.l #$C00000,a0 * VRAM先頭アドレス moveq.l #10,d7 lsl.l d7,d1 * d1.lの値を左に10ビットシフト( =d1.lに1024を掛け算する) add.l d0,d0 * d0.lの値を2倍にする add.l d0,d1 * d1.lにd0.lを足す add.l d1,a0 * a0.lにd1.lを足して描画開始VRAMアドレスの完成 * アレイモード用データの構築 lea dma_array_table,a1 move.w d3,d7 subq.w #1,d7 set_dma_table_1: move.l a0,(a1)+ * 描画先のアドレスを格納する add.l #512*2,a0 * 次のY座標に更新するために1024をa0.lに足す move.w d2,(a1)+ * 横のピクセル数を格納する dblt d7,set_dma_table_1 movem.l (sp)+,d0/d1/d7/a0/a1 rts * DMA転送が終わるまで待つ dma_wait_complete: movem.l d0/a0,-(sp) movea.l #dmaptr,a0 dma_wait_complete_1: move.b (a0),d0 and.b #$90,d0 cmp.b #0,d0 beq dma_wait_complete_1 movem.l (sp)+,d0/a0 rts * DMA転送スタート dma_start: move.l a0,-(sp) movea.l #dmaptr,a0 or.b #$80,ccr_(a0) move.l (sp)+,a0 rts * DMA転送開始前の設定 dma_setup: movem.l d0/a0/a6,-(sp) movea.l #dmaptr,a6 move.b #$ff,csr(a6) * フラグのクリアー move.b #$08,dcr(a6) * 16Bitポート move.b #$99,ocr(a6) * 転送=ワード(16Bit)単位 / オートリクエスト最大速度 move.b #%0000_01_01,scr(a6) * 転送元も転送先もレジスタの値は増加 move.b #$00,ccr_(a6) * デフォルト move.b #$000000_11,cpr(a6) * チャンネルプライオリティ : もっとも低い move.b #$05,mfc(a6) * スーパーバイザーデータ move.b #$05,dfc_(a6) * スーパーバイザーデータ move.b #$05,bfc(a6) * スーパーバイザーデータ move.l dma_height,d0 move.w d0,btc(a6) * 転送ブロックの数 lea pic,a0 move.l a0,dar(a6) lea dma_array_table,a0 move.l a0,bar(a6) movem.l (sp)+,d0/a0/a6 rts * [EOF] ------------------------------------------------------------------------------------------------ コマンドプロンプトから、 A>as DMA_A.s [Enter] A>lk DMA_A.o [Enter] A>DMA_A.x [Enter] のように入力すると DMAによって用意した画像ファイルが表示されます。 ESCキーで終了します。 [ プログラムの解説 ] 先ずは基本的な所から。 CG-VRAMは左から右に16ビット(2バイト)が横に512個並んでいてこれを1ピクセルラインと呼ぶ事にします。 ピクセルラインの最後のアドレスと次のピクセルラインの最初のアドレスは(連続に)繋がっています。 さて、本題です。 今回のDMAアレイモードのように全画面より小さい画像の描画の場合は ピクセルライン毎に描画されるピクセル範囲が繋がっていないので アレイデータ(転送情報テーブル)を作成して アレイデータの先頭アドレスをDMAレジスタに設定して描画します。 > アレイデータの一例 ---------------------------------------- (long) : $C00000 + 512×2×64 + 64×2 (word) : 256 (long) : $C00000 + 512×2×65 + 64×2 (word) : 256 (long) : $C00000 + 512×2×66 + 64×2 (word) : 256 ---------------------------------------- この例は'set_dma_table'ルーチンを実行して完成したデータの中から 256ピクセルライン中3ピクセルラインのデータを取り出したものです。 1ピクセルラインの描画に必要なメモリー容量は 描画開始アドレス4バイトと横描画ピクセル数2バイトを合わせた6バイトです。 今回はDMA標準モードにアレイデータを付けた程度なので 意外と簡単に理解できたのではないかと思います(^^) それでは、お疲れ様でした!! [EOF]